package postgres

import (
	"context"
	"fmt"
	"quipus/internal/database/model"

	apb "quipus/proto/gen/assist"

	"github.com/jackc/pgx/v5/pgconn"
	_ "github.com/jackc/pgx/v5/stdlib"
	_ "github.com/joho/godotenv/autoload"
	"github.com/pkg/errors"
	"google.golang.org/protobuf/encoding/protojson"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

func (p *PGDB) CreateUser(ctx context.Context, create *model.User) error {
	p.query()
	qdo := q.WithContext(ctx)
	err := qdo.User.Create(create)
	if err != nil {
		if pgErr, ok := err.(*pgconn.PgError); ok {
			if pgErr.Code == "23505" {
				return gorm.ErrDuplicatedKey
			}
		}
		// var pgError pgconn.PgError
		// r := errors.As(err, &pgError)
		// if r {
		// 	switch pgError.Code {
		// 	case "23505": // 唯一约束冲突
		// 		return gorm.ErrDuplicatedKey
		// 	}
		// }

		return err
	}
	return nil
}

func (p *PGDB) DeleteUser(ctx context.Context, delete *apb.DeleteUser) error {
	p.query()
	qdo := q.WithContext(ctx)
	_, err := qdo.User.Where(q.User.ID.Eq(delete.Id)).Delete()
	if err != nil {
		return err
	}
	return nil
}

func (p *PGDB) ListUsers(ctx context.Context, find *apb.FindUser) ([]*model.User, int64, error) {
	p.query()
	qdo := q.WithContext(ctx)
	user := qdo.User
	if find.Id > 0 {
		user = user.Where(q.User.ID.Eq(find.Id))
	}
	if len(find.Email) > 0 {
		user = user.Where(q.User.Email.Eq(find.Email))
	}
	if len(find.Nickname) > 0 {
		user = user.Where(q.User.Nickname.Eq(find.Nickname))
	}
	if find.Role > 0 {
		fmt.Print("check role,", find.Role.String())
		user = user.Where(q.User.Role.Eq(find.Role.String()))
	}
	if find.RowStatus > 0 {
		user = user.Where(q.User.RowStatus.Eq(find.RowStatus.String()))
	}
	if len(find.Username) > 0 {
		user = user.Where(q.User.Username.Eq(find.Username))
	}

	if find.TenantId > 0 {
		user = user.Where(q.User.TenantID.Eq(find.TenantId))
	}

	limit := find.Limit
	if limit == 0 {
		limit = 10
	}

	return user.FindByPage(int(find.Offset), int(limit))
}

func (p *PGDB) GetUser(ctx context.Context, find *apb.FindUser) (*model.User, error) {
	p.query()
	user := q.WithContext(ctx).User
	if find.Id > 0 {
		user = user.Where(q.User.ID.Eq(find.Id))
	}

	if len(find.Username) > 0 {
		user = user.Where(q.User.Username.Eq(find.Username))
	}

	if find.Role > 0 {
		user = user.Where(q.User.Role.Eq(find.Role.String()))
	}

	if find.TenantId > 0 {
		user = user.Where(q.User.TenantID.Eq(find.TenantId))
	}

	return user.First()
}

func (p *PGDB) GetUserAccessTokens(ctx context.Context, userId int32) ([]*apb.AccessTokensUserSetting_AccessToken, error) {
	p.query()
	qdo := q.WithContext(ctx)
	us := qdo.UserSetting
	if userId > 0 {
		us = us.Where(q.UserSetting.UserID.Eq(userId))
	}

	us = us.Where(q.UserSetting.Key.Eq(apb.UserSettingKey_ACCESS_TOKENS.String()))
	m, err := us.First()
	if err != nil && err != gorm.ErrRecordNotFound {
		return nil, err
	}

	token := &apb.AccessTokensUserSetting{}
	if m != nil && m.Value != "" {
		e := protojson.Unmarshal([]byte(m.Value), token)
		if e != nil {
			return nil, e
		}
	}

	return token.AccessTokens, nil
}

func (p *PGDB) ListUserSettings(ctx context.Context, userId int32) ([]*model.UserSetting, error) {
	p.query()
	qdo := q.WithContext(ctx)
	us := qdo.UserSetting
	if userId > 0 {
		us = us.Where(q.UserSetting.UserID.Eq(userId))
	}
	return us.Find()
}

func (p *PGDB) GetUserSetting(ctx context.Context, userId int32, key string) (*model.UserSetting, error) {
	p.query()
	qdo := q.WithContext(ctx)
	us := qdo.UserSetting
	if userId > 0 {
		us = us.Where(q.UserSetting.UserID.Eq(userId))
	}

	if len(key) > 0 {
		us = us.Where(q.UserSetting.Key.Eq(key))
	} else {
		return nil, errors.New("key is required")
	}
	return us.First()
}

func (p *PGDB) UpdateUser(ctx context.Context, update *apb.UpdateUser) (*model.User, error) {
	p.query()
	qdo := q.WithContext(ctx)
	if update.Id < 0 {
		return nil, errors.New("id is required")
	}

	user := qdo.User
	user = user.Where(q.User.ID.Eq(update.Id))

	updateValue := &model.User{}
	updateValue.ID = update.Id
	if update.UpdatedTs > 0 {
		updateValue.UpdatedTs = update.UpdatedTs
	}

	if update.RowStatus > 0 {
		updateValue.RowStatus = update.RowStatus.String()
	}

	if update.Username != "" {
		updateValue.Username = update.Username
	}

	if update.Role > 0 {
		updateValue.Role = update.Role.String()
	}

	if len(update.Email) > 0 {
		updateValue.Email = update.Email
	}

	if len(update.Nickname) > 0 {
		updateValue.Nickname = update.Nickname
	}

	if len(update.PasswordHash) > 0 {
		updateValue.PasswordHash = update.PasswordHash
	}

	if len(update.Avatar) > 0 {
		updateValue.AvatarURL = update.Avatar
	}

	if len(update.Description) > 0 {
		updateValue.Description = update.Description
	}

	_, e := user.Updates(updateValue)

	return updateValue, e
}

func (p *PGDB) UpsertUserSetting(ctx context.Context, upsert *model.UserSetting) error {
	p.query()
	qdo := q.WithContext(ctx)

	err := qdo.UserSetting.Clauses(clause.OnConflict{
		Columns:   []clause.Column{{Name: "user_id"}, {Name: "key"}},
		DoUpdates: clause.AssignmentColumns([]string{"user_id", "key", "value"}),
	}).Create(upsert)
	if err != nil {
		return err
	}

	return nil
}

func (p *PGDB) UpsertVirtUser(ctx context.Context, user *model.VirtUser) error {
	p.query()
	qdo := q.WithContext(ctx)

	err := qdo.VirtUser.Clauses(clause.OnConflict{
		Columns:   []clause.Column{{Name: "id"}},
		DoUpdates: clause.AssignmentColumns([]string{"id", "virt_type", "virt_id"}),
	}).Create(user)
	if err != nil {
		return err
	}

	return nil
}

func (p *PGDB) GetVirtUser(ctx context.Context, userId int32) (*model.VirtUser, error) {
	p.query()
	qdo := q.WithContext(ctx)
	virt := qdo.VirtUser
	virt = virt.Where(q.VirtUser.ID.Eq(userId))
	return virt.First()
}
func (p *PGDB) DeleteVirtUser(ctx context.Context, userId int32) error {
	p.query()
	qdo := q.WithContext(ctx)
	virt := qdo.VirtUser
	virt = virt.Where(q.VirtUser.ID.Eq(userId))
	_, err := virt.Delete()
	return err
}

func (p *PGDB) UpsertAttachUser(ctx context.Context, user *model.AttachUser) error {
	p.query()
	qdo := q.WithContext(ctx)

	err := qdo.AttachUser.Clauses(clause.OnConflict{
		Columns:   []clause.Column{{Name: "id"}},
		DoUpdates: clause.AssignmentColumns([]string{"id", "attached_id"}),
	}).Create(user)
	if err != nil {
		return err
	}
	return nil
}
func (p *PGDB) GetAttachUser(ctx context.Context, userId int32) (*model.AttachUser, error) {
	p.query()
	qdo := q.WithContext(ctx)
	attach := qdo.AttachUser
	attach = attach.Where(q.AttachUser.ID.Eq(userId))
	return attach.First()
}

func (p *PGDB) DeleteAttachUser(ctx context.Context, userId int32) error {
	p.query()
	qdo := q.WithContext(ctx)
	attach := qdo.AttachUser
	attach = attach.Where(q.AttachUser.ID.Eq(userId))
	_, err := attach.Delete()
	return err
}
